#include <muduo/net/TcpServer.h>

#include <muduo/base/Atomic.h>
#include <muduo/base/Logging.h>
#include <muduo/base/Thread.h>
#include <muduo/net/EventLoop.h>
#include <muduo/net/InetAddress.h>

#include <boost/bind.hpp>

#include <utility>

#include <stdio.h>
#include <unistd.h>

#include <muduo/base/Mutex.h>

using namespace muduo;
using namespace muduo::detail;
using namespace muduo::net;

int numThreads = 0;

class TestScop {


public:
    TestScop() {

    }

    virtual ~TestScop() {
        LOG_DEBUG << "离开作用域,自动被释放";
    }

public:
    void doSomething() {
        LOG_DEBUG << "do something \n";
    }
};

class DiscardServer {
public:


    DiscardServer(EventLoop *loop, const InetAddress &listenAddr)
            : server_(loop, listenAddr, "DiscardServer"),
              oldCounter_(0),
              startTime_(Timestamp::now()) {
        //register handle event

        //call back when connected
        server_.setConnectionCallback(
                boost::bind(&DiscardServer::onConnection, this, _1));

        //when message in
        server_.setMessageCallback(
                boost::bind(&DiscardServer::onMessage, this, _1, _2, _3));

        //thread hand  i/o
        server_.setThreadNum(numThreads);

        //间隔执行
        loop->runEvery(3.0, boost::bind(&DiscardServer::printThroughput, this));
    }

    void start() {
        LOG_INFO << "starting " << numThreads << " threads.";
        server_.start();
    }

private:

    void onConnection(const TcpConnectionPtr &conn) {
        LOG_TRACE << conn->peerAddress().toIpPort() << " -> "
                  << conn->localAddress().toIpPort() << " is "
                  << (conn->connected() ? "UP" : "DOWN");
    }

    void onMessage(const TcpConnectionPtr &conn, Buffer *buf, Timestamp) {
        size_t len = buf->readableBytes();
        //begin to decode message
        while (len >= 4) {
            size_t package_length = buf->peekInt32();
            if (len - 4 >= package_length) {
                buf->retrieve(4);
                Buffer message;
                message.append(buf->peek(), package_length);
                buf->retrieve(package_length);

                //do some logic
                string get_message(message.peek(), message.forEachByte(0));
                LOG_DEBUG << "get message:" << get_message;

                //next time to decode
                len = buf->readableBytes();
                //clear data
                //ack client,add package length
                message.prependInt32(message.readableBytes());
                conn->send(&message);
            } else {
                return;
            }
        }

        transferred_.add(len);
        receivedMessages_.incrementAndGet();
        buf->retrieveAll();
    }

    /**
     *
     */
    void printThroughput() {
        Timestamp endTime = Timestamp::now();
        int64_t newCounter = transferred_.get();
        int64_t bytes = newCounter - oldCounter_;
        int64_t msgs = receivedMessages_.getAndSet(0);
        double time = timeDifference(endTime, startTime_);
        printf("%4.3f MiB/s %4.3f Ki Msgs/s %6.2f bytes per msg\n",
               static_cast<double>(bytes) / time / 1024 / 1024,
               static_cast<double>(msgs) / time / 1024,
               static_cast<double>(bytes) / static_cast<double>(msgs));

        oldCounter_ = newCounter;
        startTime_ = endTime;
    }

    TcpServer server_;
    AtomicInt64 transferred_;
    AtomicInt64 receivedMessages_;
    int64_t oldCounter_;
    Timestamp startTime_;

};

#include "Kit.h"
#include "boost/shared_ptr.hpp"
#include "boost/scoped_ptr.hpp"

static MutexLock _mutexLock;


void test_dead_lock() {
    MutexLockGuard mutexLockGuard(_mutexLock);
    //do something
}

void test_scope_ptr() {

    //guard in stack
    //临界区域
    MutexLockGuard guard(_mutexLock);

    LOG_DEBUG << "scope_ptr test begin";
    {
        //in case forget delete
        boost::scoped_ptr<TestScop> scoped_ptr(new TestScop);
        scoped_ptr.get()->doSomething();
    }
    LOG_DEBUG << "scope_ptr test done";

    //离开作用域,guard被释放,释放互斥锁


}


void test_share_ptr(boost::shared_ptr<int> &_value) {
    LOG_DEBUG << "_value get: " << *_value << ", unique:" << _value.unique() << " ,count:" << _value.use_count();
}

#include "muduo/net/Buffer.h"
#include <stdio.h>

using namespace muduo::net;

void test_buffer() {

    //in stack buffer
    Buffer buffer;

    //writeIndex :4
    std::string temp_content("这是测试数据我呢个大艹!!!!!!!");

    LOG_DEBUG << "str length:" << strlen(temp_content.c_str());

    muduo::StringPiece stringPiece(temp_content);
    LOG_DEBUG << "测试长度:" << stringPiece.size();

    //writeIndex incr
    //字符串长度
    buffer.append(temp_content);
    buffer.appendInt8(0);//占位
    buffer.appendInt16(127);// transfer net byte order
    //write content length
    size_t content_length = buffer.readableBytes();
    //readIndex dec 4 bytes
    buffer.prependInt32(content_length);// write package length



    LOG_DEBUG << "MESSAGE DETAIL:" << buffer.toStringPiece().as_string();

    //当前数据包长度
    LOG_DEBUG << "content length:" << content_length;
    LOG_DEBUG << "package length:" << content_length + 4;

    //peek not motify it
    //read data
    LOG_DEBUG << "read content length:" << buffer.readInt32();// no motify the readIndex


    //读取中文
    //找出标记位的位置
    char value = 0;
    size_t length = buffer.forEachByte(value);
    LOG_DEBUG << "read message:" << buffer.retrieveAsString(length);
    buffer.retrieve(1);// readerIndex +1  ---- skip



    LOG_DEBUG << "***** read last short:" << buffer.readInt16();

    //clear all data
    buffer.retrieveAll();


}


#include "muduo/base/Singleton.h"

int main(int argc, char *argv[]) {

    //debug info

    Logger::setLogLevel(Logger::DEBUG);

    //test_buffer
    test_buffer();

    //test scope ptr
    test_scope_ptr();
    test_dead_lock();



    //share ptr,_value is  stack object
    boost::shared_ptr<int> _value(new int(100));
    test_share_ptr(_value);


    LOG_INFO << "pid = " << getpid() << ", tid = " << CurrentThread::tid();

    if (argc > 1) {
        numThreads = atoi(argv[1]);
    } else {
        // The number of processors currently online (available)
        numThreads = core() + 1;
        printf("_SC_NPROCESSORS_ONLN=%d\n", numThreads);
    }


    EventLoop loop;
    InetAddress port(2009);
    DiscardServer server(&loop, port);

    LOG_DEBUG << "PORT:" << 2009;


    server.start();

    //loop  forever
    loop.loop();
}

